const { add } = require("lodash")

const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'rejected' // 失败

class MyPromise {
  constructor(executor) {
    try {
      // 执行创建 Promise 时传递的函数
      executor(this.resolve, this.reject)
    } catch (e) {
      this.reject(e)
    }
  }
  // 初始状态为等待态
  status = PENDING
  // 定义成功之后的值
  successValue = undefined
  // 定义失败的原因
  failedReason = undefined
  // 成功的回调
  successCallback = []
  // 失败的回调
  failedCallback = []

  // 成功时执行的函数
  resolve = value => {
    // 当状态不是 pending 时 说明状态已经被修改过了 因为状态只能修改一次 所以这个时候需要阻止代码执行
    if (this.status !== PENDING) return
    // 将状态修改为成功
    this.status = FULFILLED
    // 保存成功的值
    this.successValue = value
    // 判断成功回调是否存在，存在则执行
    // this.successCallback && this.successCallback(value)
    // 循环执行成功回调
    while (this.successCallback.length) this.successCallback.shift()()
  }
  // 失败时执行的函数
  reject = reason => {
    // 当状态不是 pending 时 说明状态已经被修改过了 因为状态只能修改一次 所以这个时候需要阻止代码执行
    if (this.status !== PENDING) return
    // 将状态修改为失败
    this.status = REJECTED
    // 保存失败的原因
    this.failedReason = reason
    // 判断失败回调是否存在，存在则执行
    // this.failedCallback && this.failedCallback(reason)
    // 循环执行失败回调
    while (this.failedCallback.length) this.failedCallback.shift()()
  }
  // 定义 then 方法
  then(successCallback, failedCallback) {
    // 如果then或者catch方法中没有传递参数，我们需要补充一个函数，将值传递下去
    successCallback = successCallback ? successCallback : value => value
    failedCallback = failedCallback ? failedCallback : reason => { throw reason }
    let promies2 = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        setTimeout(() => {
          try {
            // 如果成功 调用成功的回调
            let x = successCallback(this.successValue)
            // 判断 x 的值是普通值还是 promise 对象
            // 普通值 直接 resolve
            // 如果是 promise 对象 查看 promise 对象返回结果
            // 成功调用 resolve 失败调用 reject
            // 这里会有一个问题，这段代码是同步执行的，同步执行的时候，promise2并不存在，
            // 需要等 executor 执行完毕之后 promise2 才会被创建出来，所以我们需要这个判断修改为异步代码
            resolvePromise(promies2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }, 0)
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            // 如果失败 调用失败的回调
            let x = failedCallback(this.failedReason)
            // 判断 x 的值是普通值还是 promise 对象
            // 普通值 直接 resolve
            // 如果是 promise 对象 查看 promise 对象返回结果
            // 成功调用 resolve 失败调用 reject
            resolvePromise(promies2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }, 0)
      } else {
        // 保存成功回调和失败回调
        this.successCallback.push(() => {
          setTimeout(() => {
            try {
              // 如果成功 调用成功的回调
              let x = successCallback(this.successValue)
              // 判断 x 的值是普通值还是 promise 对象
              // 普通值 直接 resolve
              // 如果是 promise 对象 查看 promise 对象返回结果
              // 成功调用 resolve 失败调用 reject
              resolvePromise(promies2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0)
        })
        this.failedCallback.push(() => {
          setTimeout(() => {
            try {
              // 如果失败 调用失败的回调
              let x = failedCallback(this.failedReason)
              // 判断 x 的值是普通值还是 promise 对象
              // 普通值 直接 resolve
              // 如果是 promise 对象 查看 promise 对象返回结果
              // 成功调用 resolve 失败调用 reject
              resolvePromise(promies2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0)
        })
      }
    })
    return promies2
  }
  // 捕获错误
  catch(failedCallback) {
    // 直接调用then方法，传递第二个回调
    return this.then(undefined, failedCallback)
  }
  finally(callback) {
    // 由于finally无法获取任务执行状态 但是then方法是可以知道的，
    // 所以我们调用then方法 在成功和失败的回调中都去执行finally的回调
    return this.then(value => {
      // 调用静态方法 resolve 把 callback 的返回值包装成一个 promise 对象
      return MyPromise.resolve(callback()).then(() => value)
    }, reason => {
      return MyPromise.resolve(callback()).then(() => { throw reason })
    })
  }
  static all(array) {
    // 定义结果数组
    let results = []
    // 判断array中所有任务是否执行完毕
    let index = 0
    return new Promise((resolve, reject) => {
      function addData(key, value) {
        results[key] = value
        index++
        // 当任务全部执行完毕之后才 resolve 
        if (index === array.length) {
          resolve(results)
        }
      }
      for (let i = 0; i < array.length; i++) {
        let current = array[i]
        // 如果是 promise 对象，判断是成功还是失败，失败则直接 reject
        if (current instanceof MyPromise) {
          current.then(value => addData(i, value), reject)
        } else {
          // 普通值直接添加进结果数组中
          addData(i, current)
        }
      }
    })
  }
  static race(array) {
    return new MyPromise((resolve, reject) => {
      for (let i = 0; i < array.length; i++) {
        let current = array[i]
        if (current instanceof MyPromise) {
          // Promise 对象
          current.then(resolve, reject)
        } else {
          // 普通值
          resolve(current)
        }
      }
    })
  }
  static resolve(value) {
    // 如果是Promise对象直接返回
    if (value instanceof MyPromise) return value
    // 普通值包装成Promise对象返回
    return new MyPromise(resolve => resolve(value))
  }
  static reject(value) {
    if (value instanceof MyPromise) return value
    return new MyPromise((resolve, reject) => reject(value))
  }
}
function resolvePromise(promies2, x, resolve, reject) {
  // 判断是不是promise对象本身
  if (promies2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  // 判断 x 是否是属于 promise 对象
  if (x instanceof MyPromise) {
    x.then(resolve, reject)
  } else {
    resolve(x)
  }
}
module.exports = MyPromise